using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Ruminate.DataStructures;

namespace Ruminate.GUI.Framework {

    /// <summary>
    /// Exposes the needed logic and data to the user needed to create a widget.
    /// All classes that extend this one should be GUI widgets. All of the events
    /// are triggered in the InputManager and all of the logic code is triggered 
    /// from the ElementManager. 
    /// </summary>
    public abstract class Element {

        /// <summary>
        /// Component for keeping track of location and size. Automatically run before 
        /// Element.Init is called and OffsetLocation.Init needs to be reflect to make 
        /// any changes in the resize functions. 
        /// </summary>                
        public LocationComponent LocationComponent { get; private set; }

        /// <summary>
        /// Exposure of shared SpriteBatch to render elements with.
        /// </summary>
        protected SpriteBatch SpriteBatch { get { return InstanceData.GuiBatch; } }        

        #region Internal Variables 

        /// <summary>
        /// The link to data that is shared across all elements.
        /// </summary>        
        internal InstanceData InstanceData;

        /// <summary>
        /// The link to this elements node in the tree hierarchy.
        /// </summary>
        internal TreeNode<Element> TreeNode;        

        #endregion                                             

        #region Information About the Elements State

        /// <summary>
        /// When true the element is drawn and accepts input. When false it does neither.
        /// </summary>
        public bool Visible { get; set; }

        /// <summary>
        /// When true the element handles input. When false this element ignores input.
        /// </summary>
        public bool Active { get; set; } 

        /// <summary>
        /// Returns true when this element is in focus and element is in focus and clicked.
        /// </summary>
        public bool IsPressed {
            get { return InstanceData.InputManager.PressedElement == this; }
        }

        /// <summary>
        /// Returns true when the current element is in focus.
        /// </summary>
        public bool IsFocused {
            get { return InstanceData.InputManager.FocusedElement == this; }
        }

        /// <summary>
        /// Returns true when the mouse is currently hovering over the current element
        ///  even if there is another one overlapping it.
        /// </summary>
        public bool IsHover {
            get { return InstanceData.InputManager.HoverElement == this; }
        }
        
        #endregion
        
        /// <summary>
        /// Initialize an element with the needed components.
        /// </summary>
        /// <param name="locationComponent">The component used to align 
        /// the element to its parent</param>
        protected Element(LocationComponent locationComponent) {
            
            TreeNode = new TreeNode<Element>(this);

            LocationComponent = locationComponent;
            LocationComponent.LinkedElement = this;

            Visible = true;
            Active = true;
        }

        /// <summary>
        /// Initialization of for data not know at initialization time. Called when the 
        /// element is attached to the root node.
        /// </summary>
        /// <param name="instanceData">The link to data than needs to be shared 
        /// across all elements</param>
        internal void InternalInit(InstanceData instanceData) {

            InstanceData = instanceData;            
            Init();
        }

        /*####################################################################*/
        /*                           Tree Management                          */
        /*####################################################################*/

        #region Tree Management

        /// <summary>
        /// Returns the current element's parent
        /// </summary>              
        public Element Parent { get { return TreeNode.Parent.Data; } }

        /// <summary>
        /// Returns the children of this element as a list
        /// </summary>
        public List<Element> Children {
            get { return TreeNode.Children.Select(treeNode => treeNode.Data).ToList(); }
        }

        /// <summary>
        /// Add a child element to this element 
        /// </summary>
        /// <param name="element">The element to add</param>
        public void AddChild(Element element) {
            
            TreeNode.AddChild(element.TreeNode);

            if (InstanceData != null) {
                ElementManager.InitTree(element.TreeNode);                
            }
            
        }

        /// <summary>
        /// Remove the specified element from this element's children
        /// </summary>
        /// <param name="element">The element to remove</param>
        public void RemoveChild(Element element) {

            TreeNode.RemoveChild(element);
        }

        #endregion

        /*####################################################################*/
        /*                                 Events                             */
        /*####################################################################*/

        #region Events

        protected internal virtual void EnterHover(MouseEventArgs e) { }
        protected internal virtual void ExitHover(MouseEventArgs e) { }
        protected internal virtual void EnterPressed(MouseEventArgs e) { }
        protected internal virtual void ExitPressed(MouseEventArgs e) { }
        protected internal virtual void EnterFocus(MouseEventArgs e) { }
        protected internal virtual void ExitFocus(MouseEventArgs e) { }

        protected internal virtual void CharEntered(CharacterEventArgs e) { }
        protected internal virtual void KeyDown(KeyEventArgs e) { }
        protected internal virtual void KeyUp(KeyEventArgs e) { }
        protected internal virtual void MouseClick(MouseEventArgs e) { }
        protected internal virtual void MouseDoubleClick(MouseEventArgs e) { }
        protected internal virtual void MouseDown(MouseEventArgs e) { }
        protected internal virtual void MouseHover(MouseEventArgs e) { }
        protected internal virtual void MouseMove(MouseEventArgs e) { }
        protected internal virtual void MouseUp(MouseEventArgs e) { }
        protected internal virtual void MouseWheel(MouseEventArgs e) { }

        #endregion

        /*####################################################################*/
        /*                         Extendable Methods                         */
        /*####################################################################*/                

        #region Exposed Logic

        //Called when needed due to element being resized resize
        protected internal abstract void Render();
        protected internal abstract Rectangle ChildRenderArea();   

        //Logic Loop
        protected internal abstract void Init();
        protected internal abstract void Update();
        protected internal abstract void Draw();

        #endregion
    }
}
